package com.googlecode.dgwt.client.dojo.data.base;

import java.util.Collection;
import java.util.Collections;

import com.googlecode.dgwt.client.dojo.Console;
import com.googlecode.dgwt.client.dojo.data.listener.DataListener;
import com.googlecode.dgwt.client.dojo.exception.AttachException;

/**
 * @author wellsantos@wellsantos.com
 * @since 0.01
 */
public class MirroredStoreEngine<Item> extends StoreEngine<Item> implements DataListener<Item> {

    private boolean listenersAttached = false;

    
    public MirroredStoreEngine(Store<Item> parentStore) {
        super(parentStore, 0);
    }

    public MirroredStoreEngine(Store<Item> parentStore, Store<Item> mirroredStore) {
        this(parentStore);
        setMirroredStore(mirroredStore);
    }

    
    public void setMirroredStore(Store<Item> mirroredStore){
        Store<Item> oldStore = getMirroredStore();
        if (mirroredStore != oldStore){
            if (oldStore != null){
                oldStore.removeListener(this);
            }
            if (mirroredStore != null 
                    && (!(mirroredStore instanceof StoreBase<?>) 
                            || ((StoreBase<Item>) mirroredStore).getEngine() != this)){
                mirroredStore.addListener(this);
            }
        }
    }
    
    public Store<Item> getMirroredStore(){
        Collection<Item> store = getItemList();
        if (store instanceof Store<?>){
            return (Store<Item>) store;
        }
        return null;
    }
    
    protected boolean isListenersAttached() {
        return listenersAttached;
    }

    @Override
    protected Collection<Item> createItemList(Store<Item> store, int initialCapacity) {
        return Collections.emptyList();
    }

    @Override
    protected StoreListenerList<Item, DataListener<Item>> createListenerList(Store<Item> store) {
        return new StoreListenerList<Item, DataListener<Item>>(store){

            @Override
            protected void notifyAttach(DataListener<Item> listener) throws AttachException {
                if (isListenersAttached()){
                    super.notifyAttach(listener);
                }
            }

            @Override
            protected void notifyDetach(DataListener<Item> listener) {
                if (isListenersAttached()){
                    super.notifyDetach(listener);
                }
            }

        };
    }

    @Override
    public void onAttach(Store<Item> parent) throws AttachException {
        Store<Item> store = getMirroredStore();
        if (store != parent){
            assert(store == null) : "Cannot use the same MirroredStoreEngine for two different stores";
            notifyDetach();
            setItemList(parent);
            notifyAttach();
        }        
    }

    @Override
    public void onDetach(Store<Item> parent) {
        if (getItemList() == parent){
            setItemList(Collections.<Item>emptyList());
        }
    }

    private void notifyAttach() {
        if (!isListenersAttached()){
            try {
                for (DataListener<Item> listener : getDataListenerList()){
                    try {
                        listener.onAttach(getDataListenerList().getParent());
                    } catch (AttachException e) {
                        Console.error(e);
                        continue;
                    }
                }
            } finally {
                listenersAttached = true;
            }
        }
    }

    private void notifyDetach() {
        if (isListenersAttached()){
            listenersAttached = false;
            for (DataListener<Item> listener : getDataListenerList()){
                listener.onDetach(getDataListenerList().getParent());
            }
        }
    }


    // Item Operations

    
    @Override
    public Item get(int index) {
        Collection<Item> store = getItemList();
        if (store instanceof StoreBase<?>){
            return ((StoreBase<Item>)store).getEngine().get(index);
        }
        throw new UnsupportedOperationException();
    }

    @Override
    public void add(int index, Item element) {
        Collection<Item> store = getItemList();
        if (store instanceof StoreBase<?>){
            ((StoreBase<Item>)store).getEngine().add(index, element);
            return;
        }
        throw new UnsupportedOperationException();
    }

    @Override
    public Item remove(int index) {
        Collection<Item> store = getItemList();
        if (store instanceof StoreBase<?>){
            return ((StoreBase<Item>)store).getEngine().remove(index);
        }
        throw new UnsupportedOperationException();
    }

    @Override
    public Item set(int index, Item element) {
        Collection<Item> store = getItemList();
        if (store instanceof StoreBase<?>){
            return ((StoreBase<Item>)store).getEngine().set(index, element);
        }
        throw new UnsupportedOperationException();
    }

}
